#pragma once

#include <boost/functional/hash.hpp>
#include <angelscript.h>

#include "exports.hpp"




namespace Engine
{
	namespace Script
	{
		template <typename T>
		class pointer
		{
		public:

			/**
			 * Default constructs a smart pointer.
			 * It points to nothing.
			 */
			pointer()
				: foo(nullptr)
			{}

			/**
			 * Constructs a smart pointer from the given object.
			 * Takes ownership of it and assumes the current reference count
			 * equals 1.
			 */
			explicit pointer(T* foo)
				: foo(foo)
			{}

			/**
			 * Copy constructor for a pointer using another type.
			 */
			template <typename Y>
			pointer(const pointer<Y>& that)
				: foo(static_cast<T*>(that.get()))
			{
				if(foo)
					foo->AddRef();
			}

			pointer(const pointer<T>& that)
				: foo(that.foo)
			{
				if(foo)
					foo->AddRef();
			}

			/**
			 * Releases the held reference counted object.
			 */
			~pointer()
			{
				if(foo)
					foo->Release();
			}



			/**
			 * The next time if forget the assignment operator I should be beaten to death with a stick.
			 */
			template <typename Y>
			pointer<T>& operator=(const pointer<Y>& that)
			{
				foo = static_cast<T*>(that.get());
				if(foo)
					foo->AddRef();

				return *this;
			}

			/**
			 * The next time if forget the assignment operator I should be beaten to death with a stick.
			 */
			pointer<T>& operator=(const pointer<T>& that)
			{
				foo = that.foo;
				if(foo)
					foo->AddRef();

				return *this;
			}



			/**
			 * @returns the stored pointer
			 */
			T* get() const
			{
				return foo;
			}

			/**
			 * @returns the stored pointer
			 */
			T* operator->() const
			{
				return foo;
			}



			/**
			 * Compares this pointer against another one.
			 */
			bool operator==(const pointer<T>& that) const
			{
				return foo == that.foo;
			}

			/**
			 * Compares this held object against a raw pointer.
			 */
			bool operator==(T* other) const
			{
				return foo == other;
			}

			/**
			 * Compares this held object against a raw pointer.
			 */
			bool operator!=(T* other) const
			{
				return foo != other;
			}

			/**
			 * Tests if the stored pointer is a nullptr or not.
			 */
			operator bool() const
			{
				return foo != nullptr;
			}

		private:

			T* foo;
		};
		///////////////////////////////////////////////////////////////////////////////////////////

		//We'll export those templates so they can easily be used as members in exported
		// classes...
		template class ENGINE_SCRIPT_API pointer<asIScriptEngine>;
		template class ENGINE_SCRIPT_API pointer<asIObjectType>;
		template class ENGINE_SCRIPT_API pointer<asIScriptObject>;
		template class ENGINE_SCRIPT_API pointer<asIScriptFunction>;



		template <typename T>
		std::size_t hash_value(const pointer<T>& that)
		{
			return boost::hash_value(that.get());
		}
		///////////////////////////////////////////////////////////////////////////////////////////
	}
	///////////////////////////////////////////////////////////////////////////////////////////////
}
///////////////////////////////////////////////////////////////////////////////////////////////////
